home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue24 / ntserv / Services.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1997-07-04  |  15.0 KB  |  522 lines

  1. unit Services;
  2.  
  3. interface
  4.  
  5. { If you activate this define the startup for service 2c will add a delay of   }
  6. { 45 seconds to DoServiceStartup and 30 seconds to DoServiceClose. It also     }
  7. { causes NeedExtnededElapseTime to return True if the current state is         }
  8. { SERVICE_START_PENDING or SERVICE_STOP_PENDING. This is to test the auto      }
  9. { update of the SCM status                                                     }
  10.  
  11. {.$DEFINE TESTLENGTHYOPERATIONS}
  12.  
  13. uses Windows, Messages, SysUtils, Classes, SvcClass, WinSvcX, Logging;
  14.  
  15. type
  16.   TNTServiceControllerDemo = class(TNTServiceController)
  17.   private
  18.     FCriticalSection: TRTLCriticalSection;
  19.     FFileCopyDetails: TStrings;
  20.   public
  21.     constructor Create; override;
  22.     destructor Destroy; override;
  23.   end;
  24.  
  25. type
  26.   TService2a = class(TNTService)
  27.   private
  28.     FBeepDelay: Integer;
  29.   protected
  30.     function AcceptPause: Boolean; override;
  31.     procedure DoServiceProcessing; override;
  32.     procedure ProcessParms(Parms: TStrings); override;
  33.   public
  34.     class function ServiceDisplayName: Shortstring; override;
  35.     class function ServiceName: Shortstring; override;
  36.   end;
  37.  
  38. type
  39.   TService2b = class(TNTService)
  40.   private
  41.     FFileList: TStrings;
  42.     FFileExtensions: TStrings;
  43.     FMonitorDirectory: string;
  44.     FDriveChangeHandle: THandle;
  45.     procedure AddToLog(const Text: string);
  46.   protected
  47.     function AcceptPause: Boolean; override;
  48.     procedure DoServiceProcessing; override;
  49.     procedure DoHandlerNotification; override;
  50.     procedure ProcessParms(Parms: TStrings); override;
  51.   public
  52.     constructor Create(Parms: TStrings; Controller: TNTServiceController); override;
  53.     destructor Destroy; override;
  54.     class function ServiceDisplayName: Shortstring; override;
  55.     class function ServiceName: Shortstring; override;
  56.   end;
  57.  
  58. const
  59.   BufferSize = 32768;
  60.  
  61. type
  62.   TService2c = class(TNTService)
  63.   private
  64.     FConnectEvent: THandle;
  65.     FInBuffer: array[0..255] of char;
  66.     FOutBuffer: array[0..BufferSize-1] of char;
  67.     FOverlap: TOverlapped;
  68.     FPendingIO: Boolean;
  69.     FPipe: THandle;
  70.     FSecurityDesc: TSecurityAttributes;
  71.     FState: DWORD;
  72.     FSuccess: Boolean;
  73.     FWaitStatus: DWORD;
  74.   protected
  75.     procedure DoHandlerNotification; override;
  76.     procedure DoServiceCloseDown; override;
  77.     procedure DoServiceProcessing; override;
  78.     procedure DoServiceStartup; override;
  79.     function NeedExtnededElapseTime(Option: DWORD): Boolean; override;
  80.   public
  81.     class procedure DependentServices(List: TStrings); override;
  82.     class function ServiceDisplayName: Shortstring; override;
  83.     class function ServiceName: Shortstring; override;
  84.   end;
  85.  
  86. implementation
  87.  
  88. function DirectoryExists(const DirName: string): Boolean;
  89. var
  90.   SRec: TSearchRec;
  91.  
  92. begin
  93.   SRec.FindHandle := FindFirstFile(PChar(DirName + '\*.*'),SRec.FindData);
  94.   Result := SRec.FindHandle <> INVALID_HANDLE_VALUE;
  95.   if Result then
  96.     FindClose(SRec);
  97. end;
  98.  
  99. {==============================================================================}
  100.  
  101. constructor TNTServiceControllerDemo.Create;
  102. begin
  103.   InitializeCriticalSection(FCriticalSection);
  104.   FFileCopyDetails := TStringList.Create;
  105.   inherited Create;
  106. end;
  107.  
  108. destructor TNTServiceControllerDemo.Destroy;
  109. begin
  110.   FFileCopyDetails.Free;
  111.   DeleteCriticalSection(FCriticalSection);
  112.   inherited Destroy;
  113. end;
  114.  
  115. {==============================================================================}
  116.  
  117. procedure TService2a.DoServiceProcessing;
  118. begin
  119.   While not Terminated do
  120.     begin
  121.       case CurrentState of
  122.         SERVICE_RUNNING:
  123.           begin
  124.             MessageBeep(0);
  125.           end;
  126.         SERVICE_PAUSE_PENDING:
  127.           begin
  128.             CurrentState := SERVICE_PAUSED;
  129.           end;
  130.         SERVICE_CONTINUE_PENDING:
  131.           begin
  132.             CurrentState := SERVICE_RUNNING;
  133.           end;
  134.       end;
  135.       Sleep(FBeepDelay);
  136.     end;
  137. end;
  138.  
  139. function TService2a.AcceptPause: Boolean;
  140. begin
  141.   Result := True;
  142. end;
  143.  
  144. procedure TService2a.ProcessParms(Parms: TStrings);
  145. var
  146.   Delay: Integer;
  147.  
  148. begin
  149.   FBeepDelay := 2000;
  150.   if Parms.Count > 0 then
  151.     try
  152.       Delay := StrToInt(Parms[Parms.Count - 1]);
  153.       if Delay < 500 then
  154.         FBeepDelay := 500
  155.       else
  156.         if Delay > 10000 then
  157.           FBeepDelay := 10000
  158.         else
  159.           FBeepDelay := Delay;
  160.     except
  161.       { Nothing - accept default of1 second }
  162.     end;
  163. end;
  164.  
  165. class function TService2a.ServiceDisplayName: Shortstring;
  166. begin
  167.   Result := 'Demonstration service 2a';
  168. end;
  169.  
  170. class function TService2a.ServiceName: Shortstring;
  171. begin
  172.   Result := 'DemoService2a';
  173. end;
  174.  
  175. {==============================================================================}
  176.  
  177. procedure ReadFileNames(const Directory,Mask: string; FileList: TStrings; RemoveExtension: Boolean);
  178. var
  179.   Status: Integer;
  180.   SearchRec: TSearchRec;
  181.   WildCard: string;
  182.   I: Integer;
  183.   S: string;
  184.  
  185. begin
  186.   if Mask = '' then
  187.     WildCard := '*.*'
  188.   else
  189.     WildCard := Mask;
  190.   Status := FindFirst(Directory+'\'+WildCard,faAnyFile,SearchRec);
  191.   try
  192.     while Status = 0 do
  193.       begin
  194.         if (SearchRec.Attr and $1F = 0) then
  195.           FileList.Add(SearchRec.Name);
  196.         Status := FindNext(SearchRec);
  197.       end; { While }
  198.   finally
  199.     FindClose(SearchRec);
  200.   end;
  201.   if RemoveExtension then
  202.     for I := 0 to FileList.Count - 1 do
  203.       begin
  204.         S := FileList[I];
  205.         System.Delete(S,Pos('.',S),4);
  206.         FileList[I] := S;
  207.       end;
  208. end;
  209.  
  210. {==============================================================================}
  211.  
  212. constructor TService2b.Create(Parms: TStrings; Controller: TNTServiceController);
  213. begin
  214.   FFileExtensions := TStringList.Create;
  215.   FFileExtensions.Add('*.*');
  216.   FFileList := TStringList.Create;
  217.   FMonitorDirectory := 'C:\TEMPX';
  218.   if not DirectoryExists(FMonitorDirectory) then
  219.     CreateDirectory(PChar(FMonitorDirectory),nil);
  220.   CreateDirectory(PChar(FMonitorDirectory+'\SVBACKUP'),nil);
  221.   inherited Create(Parms,Controller);
  222. end;
  223.  
  224. destructor TService2b.Destroy;
  225. begin
  226.   FFileExtensions.Free;
  227.   FFileList.Free;
  228.   inherited Destroy;
  229. end;
  230.  
  231. function TService2b.AcceptPause: Boolean;
  232. begin
  233.   Result := True;
  234. end;
  235.  
  236. procedure TService2b.AddToLog(const Text: string);
  237. begin
  238.   EnterCriticalSection(TNTServiceControllerDemo(Controller).FCriticalSection);
  239.   try
  240.     TNTServiceControllerDemo(Controller).FFileCopyDetails.Add(Text);
  241.   finally
  242.     LeaveCriticalSection(TNTServiceControllerDemo(Controller).FCriticalSection);
  243.   end;
  244. end;
  245.  
  246. procedure TService2b.DoHandlerNotification;
  247. begin
  248.   inherited DoHandlerNotification;
  249.   PostThreadMessage(ThreadId,WM_USER,0,0);
  250. end;
  251.  
  252. procedure TService2b.DoServiceProcessing;
  253. var
  254.   WaitStatus: Integer;
  255.   I: Integer;
  256.   FSource,FDest: string;
  257.   PutTimeOut: Boolean;
  258.   Msg: TMsg;
  259.  
  260.   procedure CloseDirectoryNotification;
  261.   begin
  262.     FindCloseChangeNotification(FDriveChangeHandle);
  263.     FDriveChangeHandle := 0;
  264.   end;
  265.  
  266.   procedure SetupDirectoryNotification;
  267.   begin
  268.     FDriveChangeHandle := FindFirstChangeNotification(PChar(FMonitorDirectory),False,
  269.                              FILE_NOTIFY_CHANGE_FILE_NAME or FILE_NOTIFY_CHANGE_DIR_NAME or
  270.                              FILE_NOTIFY_CHANGE_SIZE or FILE_NOTIFY_CHANGE_LAST_WRITE);
  271.   end;
  272.  
  273. begin
  274.   While not Terminated do
  275.     begin
  276.       case CurrentState of
  277.         SERVICE_RUNNING:
  278.           begin
  279.             if FDriveChangeHandle = 0 then
  280.               SetupDirectoryNotification;
  281.             WaitStatus := MsgWaitForMultipleObjects(1,FDriveChangeHandle,False,Infinite,QS_POSTMESSAGE);
  282.             case WaitStatus of
  283.               WAIT_OBJECT_0: { FindFirstChangeNotification has been fired }
  284.                 begin
  285.                   PutTimeOut := False;
  286.                   FFileList.Clear;
  287.                   ReadFileNames(FMonitorDirectory,FFileExtensions[0],FFileList,False);
  288.                   for I := 0 to FFileList.Count - 1 do
  289.                     begin
  290.                       FSource := FMonitorDirectory+'\'+FFileList[I];
  291.                       if GetFileAttributes(PChar(FSource)) and FILE_ATTRIBUTE_ARCHIVE <> 0 then
  292.                         begin
  293.                           if not PutTimeOut then
  294.                             begin
  295.                               AddToLog(Format('Directory %s was modified at %s',[FMonitorDirectory,FormatDateTime('hh:nn:ss dd/mm/yy',Now)]));
  296.                               PutTimeOut := True;
  297.                             end;
  298.                           AddToLog(Format(' File %s was copied.',[FFileList[I]]));
  299.                           FDest := FMonitorDirectory+'\SVBACKUP\'+FFileList[I];
  300.                           CopyFile(PChar(FSource),PChar(FDest),False);
  301.                           SetFileAttributes(PChar(FSource),FILE_ATTRIBUTE_NORMAL);
  302.                         end;
  303.                     end;
  304.                   if not Terminated then
  305.                     FindNextChangeNotification(FDriveChangeHandle);
  306.                 end;
  307.               WAIT_OBJECT_0 + 1: { A message was posted to the thread - just discard it }
  308.                 GetMessage(Msg,0,0,0);
  309.             end;
  310.           end;
  311.         SERVICE_PAUSE_PENDING:
  312.           begin
  313.             if FDriveChangeHandle <> 0 then
  314.               CloseDirectoryNotification;
  315.             CurrentState := SERVICE_PAUSED;
  316.           end;
  317.         SERVICE_CONTINUE_PENDING:
  318.           begin
  319.             CurrentState := SERVICE_RUNNING;
  320.           end
  321.       else
  322.         Sleep(1000);
  323.       end;
  324.     end;
  325. end;
  326.  
  327. procedure TService2b.ProcessParms(Parms: TStrings);
  328. begin
  329.   if Parms.Count = 1 then
  330.     FFileExtensions.Assign(Parms);
  331. end;
  332.  
  333. class function TService2b.ServiceDisplayName: Shortstring;
  334. begin
  335.   Result := 'Demonstration service 2b';
  336. end;
  337.  
  338. class function TService2b.ServiceName: Shortstring;
  339. begin
  340.   Result := 'DemoService2b';
  341. end;
  342.  
  343. {==============================================================================}
  344.  
  345. const
  346.   NumInstances = 1;
  347.   ClientTimeOut = 2000;
  348.  
  349. const
  350.   WAITING_FOR_CONNECT_COMPLETE = 0;
  351.   WAITING_FOR_READ_COMPLETE    = 1;
  352.   WAITING_FOR_WRITE_COMPLETE    = 2;
  353.  
  354. class procedure TService2c.DependentServices(List: TStrings);
  355. begin
  356.   List.Add(TService2b.ServiceName);
  357. end;
  358.  
  359. procedure TService2c.DoHandlerNotification;
  360. begin
  361.   inherited DoHandlerNotification;
  362.   PostThreadMessage(ThreadId,WM_USER,0,0);
  363. end;
  364.  
  365. procedure TService2c.DoServiceCloseDown;
  366. begin
  367.   DisconnectNamedPipe(FPipe);
  368.   CloseHandle(FPipe);
  369.   CloseHandle(FConnectEvent);
  370.   FreeMem(FSecurityDesc.lpSecurityDescriptor,SECURITY_DESCRIPTOR_MIN_LENGTH);
  371. {$IFDEF TESTLENGTHYOPERATIONS}
  372.   Sleep(30000);
  373. {$ENDIF}
  374. end;
  375.  
  376. procedure TService2c.DoServiceProcessing;
  377. var
  378.   cbBytes: DWORD;
  379.   Command: string;
  380.   Msg: TMsg;
  381.  
  382.   procedure BuildQueryData;
  383.   var
  384.     Stream: TStream;
  385.  
  386.   begin
  387.     EnterCriticalSection(TNTServiceControllerDemo(Controller).FCriticalSection);
  388.     try
  389.       if TNTServiceControllerDemo(Controller).FFileCopyDetails.Count = 0 then
  390.         StrPCopy(FOutBuffer,'There is no data currently')
  391.       else
  392.         begin
  393.           Stream := TMemoryStream.Create;
  394.           try
  395.             TNTServiceControllerDemo(Controller).FFileCopyDetails.SaveToStream(Stream);
  396.             Stream.Position := 0;
  397.             Stream.ReadBuffer(FOutBuffer,Stream.Size);
  398.           finally
  399.             Stream.Free;
  400.           end;
  401.         end;
  402.     finally
  403.       LeaveCriticalSection(TNTServiceControllerDemo(Controller).FCriticalSection);
  404.     end;
  405.   end;
  406.  
  407.   procedure ResetQueryData;
  408.   begin
  409.     EnterCriticalSection(TNTServiceControllerDemo(Controller).FCriticalSection);
  410.     try
  411.       TNTServiceControllerDemo(Controller).FFileCopyDetails.Clear;
  412.     finally
  413.       LeaveCriticalSection(TNTServiceControllerDemo(Controller).FCriticalSection);
  414.     end;
  415.     StrPCopy(FOutBuffer,'Data was reset')
  416.   end;
  417.  
  418.   procedure StartConnect(Disconnect: Boolean);
  419.   begin
  420.     if Disconnect then
  421.       DisconnectNamedPipe(FPipe);
  422.     ConnectNamedPipe(FPipe,@FOverlap);
  423.     case GetLastError of
  424.       ERROR_IO_PENDING:
  425.           FState := WAITING_FOR_CONNECT_COMPLETE;
  426.       ERROR_PIPE_CONNECTED:
  427.         begin
  428.           FState := WAITING_FOR_READ_COMPLETE;
  429.           SetEvent(FConnectEvent);
  430.         end;
  431.     end;
  432.   end;
  433.  
  434. begin
  435.   StartConnect(False);
  436.   While not Terminated do
  437.     begin
  438.       FWaitStatus := MsgWaitForMultipleObjects(1,FConnectEvent,False,Infinite,QS_POSTMESSAGE);
  439.       case FWaitStatus of
  440.         WAIT_OBJECT_0:
  441.           case FState of
  442.             WAITING_FOR_CONNECT_COMPLETE:
  443.               begin
  444.                 ReadFile(FPipe,FInBuffer,Sizeof(FInBuffer),cbBytes,@FOverlap);
  445.                 if GetLastError = ERROR_IO_PENDING then
  446.                   FState := WAITING_FOR_READ_COMPLETE
  447.                 else
  448.                   StartConnect(True);
  449.               end;
  450.             WAITING_FOR_READ_COMPLETE:
  451.               begin
  452.                 Command := StrPas(FInBuffer);
  453.                 if Command = 'RESET' then
  454.                   ResetQueryData
  455.                 else
  456.                   if Command = 'QUERY' then
  457.                     BuildQueryData
  458.                   else
  459.                     StrPCopy(FOutBuffer,'Unknown command option!');
  460.                 WriteFile(FPipe,FOutBuffer,StrLen(FOutBuffer),cbBytes,@FOverlap);
  461.                 if GetLastError = ERROR_IO_PENDING then
  462.                   FState := WAITING_FOR_WRITE_COMPLETE
  463.                 else
  464.                   StartConnect(True);
  465.               end;
  466.             WAITING_FOR_WRITE_COMPLETE:
  467.               begin
  468.                 StartConnect(True);
  469.               end;
  470.           end;
  471.         WAIT_OBJECT_0 + 1: { A message was posted to the thread }
  472.           GetMessage(Msg,0,0,0);
  473.       end;
  474.     end;
  475. end;
  476.  
  477. procedure TService2c.DoServiceStartup;
  478. begin
  479.   With FSecurityDesc do
  480.     begin
  481.       nLength := Sizeof(FSecurityDesc);
  482.       lpSecurityDescriptor := AllocMem(SECURITY_DESCRIPTOR_MIN_LENGTH);
  483.       InitializeSecurityDescriptor(lpSecurityDescriptor,SECURITY_DESCRIPTOR_REVISION);
  484.       SetSecurityDescriptorDacl(lpSecurityDescriptor,True,nil,False);
  485.     end;
  486.   FConnectEvent := CreateEvent(nil,True,True,nil);
  487.   FOverlap.hEvent := FConnectEvent;
  488.   FPipe := CreateNamedPipe('\\.\Pipe\Service2b',
  489.                            PIPE_ACCESS_DUPLEX or FILE_FLAG_OVERLAPPED,
  490.                            PIPE_TYPE_MESSAGE or PIPE_READMODE_MESSAGE or PIPE_WAIT,
  491.                            1,
  492.                            BufferSize,
  493.                            BufferSize,
  494.                            ClientTimeout,
  495.                            @FSecurityDesc);
  496. {$IFDEF TESTLENGTHYOPERATIONS}
  497.   Sleep(45000);
  498. {$ENDIF}
  499. end;
  500.  
  501. function TService2c.NeedExtnededElapseTime(Option: DWORD): Boolean;
  502. begin
  503. {$IFDEF TESTLENGTHYOPERATIONS}
  504.   Result := (Option = SERVICE_START_PENDING) or (Option = SERVICE_STOP_PENDING);
  505. {$ELSE}
  506.   Result := False;
  507. {$ENDIF}
  508. end;
  509.  
  510. class function TService2c.ServiceDisplayName: Shortstring;
  511. begin
  512.   Result := 'Demonstration service 2c';
  513. end;
  514.  
  515. class function TService2c.ServiceName: Shortstring;
  516. begin
  517.   Result := 'DemoService2c';
  518. end;
  519.  
  520. end.
  521.  
  522.